No internet connection
  1. Home
  2. Tutorials

How to use keyboard modifier keys with Stream Deck and other devices to create multifunctional buttons and "layers".

SoundFlow always know what the state of your modifier keys are on your computer keyboard.

This means that if a script is triggered by for instance a Stream Deck button, the scripts behaviour can changed depending on the state of your keyboard
You could also use this technique with MIDI controllers or other HID devices, which can open up what you can do with every device that takes up your precious desk space.

Here's a script for you to test:

if (event.keyboardState.asString == 'shift')
    log('Only shift is pressed down', '')
else if (event.keyboardState.hasShift)
    log('Shift is pressed down and potentially other modifiers', 'Has keyboard state: ' + event.keyboardState.asString)
else
    log('Shift is not is pressed','')

Try running the script above from the Run Command button or from one of your devices while holding different modifier keys down.

You can also have a script work for multiple different keyboard triggers. Try add the following Keyboard triggers to the script above:

..
You can make your logic more or less complicated through combining the different modifiers.

Please feel free to ask if there's something you don't understand. Happy workflow :)

  • 27 replies

There are 27 replies. Estimated reading time: 26 minutes

  1. Jesper, this is great! Thank you

    1. In reply toJesperA⬆:

      I use modifiers for my track presets deck. I have a track preset for the most common instrument types (K, SN Kit, Bass E. GTR, AC GTR, Voc, BGV, etc). Once I import the client's tracks into the bottom of my template, pressing a preset assigns it to the selected track. If I hold shift, it assigns the preset then moves the track up into my template next to the corresponding VCA. If I hold ctrl It just moves the track next to the VCA, and finally, if I hold cmd, it keeps the inserts and sends from the track (put there by the client/artist), but assigns everything else from the preset (output routing, track color, and group) the moves it into my template next to the proper VCA.

      One 15 key deck, 45 functions. My session prep has gone from 30-45 mins to 5- 10 mins

      1. Jesper Ankarfeldt @JesperA2020-11-21 17:59:20.492Z2020-11-22 01:36:58.278Z

        Nice Chris. That's exactly what I'm talking about 👏

        I just experimented with the streamdeck being dynamic depending on the modifier keys pressed.
        I have an advanced script to navigate my projet folder, copying or moving files to and between them and to my google drive. So the different combinations of modifiers started to get a little crazy so I thought a visual feedback would be good.

        I'm doing it with a script running in the background. But it's obviously not the smoothest way to do it. Maybe it could be a feature down the line for SF, to have an option to make text or icons dynamic on stream deck that way...
        Here's a preview of the streamdeck text shift when I press different modifiers: https://youtu.be/TPC6FRuayDg

        1. That's cool. I'm assuming that the background script is waiting for modifiers and then switches decks when it sees them.
          How does the background script know what deck you're on?

          1. The code is pretty simple. This is it. Still testing though:

            .
            And then a whole bunch of decks:

            .

            So yeah not the smoothest way. It's easy to imagine that there could be a smoother implementation to allow something similar in the future.

      2. In reply toJesperA⬆:

        I noticed that the first script in the tutorial doesn't recognize the ctrl key. However I'm able to use ctrl as an event modifier in my script. Any idea why?

        1. I assume you are pressing the Run Command with the mouse.
          It's because holding control down and then clicking the left mouse click, results in a mouse right click :)

          1. Ahhh! Good catch.

        2. A
          In reply toJesperA⬆:
          Andrew Downes @Andrew_Downes
            2020-11-26 05:31:08.196Z

            How do you get the icons to change when pushing the modifier keys, like in your video.

            1. Hey Andrew.

              So it's not "natively" supported in SoundFlow, meaning that you will have to use a script it order to make them shift.
              This will probably work better if we could trigger a script with only a modifier key. I'm sure it will come at some point.
              Until then, here's the script you need with a bit less decks, so it's easy for you to copy in.

              I've attached a screenshot of the script earlier. In that screenshot you can also see that the script needs to be triggered by a finder program trigger.

              Let me know if you have any problems making it work. Best

              var app = sf.ui.finder
              
              // Copy in the Deck's command ID you wanna use. You can do this by selecting the package and press cmd+c or from the menu line.  
              var normal = 'package:cjybggh2v00002s10ucoqeja9' 
              var cmd = 'package:ckhqmdfjs0006wi10g05isll4'
              var ctrl = 'package:ckhqmep7a0009wi108ci093sr'
              
              // If finder is unhiden start loop
              sf.engine.runInBackground(function () {
                  while (true) {
                      // This checks for a user cancellation.
                      sf.engine.checkForCancellation();
              
                      // This will break the loop if finder is not in focus 
                      if (sf.ui.frontmostApp.activeBundleID != app.activeBundleID)
                          break;
              
                      // Checking what streamdeck to load
                      if (event.keyboardState.asString == 'ctrl') sf.soundflow.runCommand({ commandId: ctrl })
                      else if (event.keyboardState.asString == 'cmd') sf.soundflow.runCommand({ commandId: cmd })
                      else sf.soundflow.runCommand({ commandId: normal })
              
                      // This will do so we check every 100 ms if there's been a "change"
                      sf.wait({
                          executionMode: 'Background',
                          intervalMs: 100,
                          onError: 'Continue'
                      });
                  }
              });
              
            2. A
              In reply toJesperA⬆:
              Andrew Downes @Andrew_Downes
                2020-11-27 02:13:44.490Z

                It seems as if this is well above my understanding of javascript, which is basic at best.
                I have it working as I want it to, just without the visual aid, after adding in all the bits of code that I didn't have, now nothing happens at all.
                I am assuming you're using the Steamdeck from within SoundFlow as opposed to the Streamdeck app?
                Thank you for your help

                1. Hey Andrew.

                  Yes it can only be done without using the Streamdeck app. I would not recommend anyone using it if they use SoundFlow.

                  So not sure how to help you. Just some pointers:

                  • I think it's best to create two separate scripts. One that controls your commands and one that's only controlling the visual part (with my script as above and nothing more).
                  • You can check wether you have the right Deck ID, by making a new script and just run sf.soundflow.runCommand({ commandId: /*Deck ID as a string*/ })
                  • To check wether the script is running you can add log('The script is running') before the sf.wait() part. I recommend changing the interval to 500 then, else you will get a lot of notifications.

                  If you want more precise help you'll have to make a video or take some snapshots of your scripts etc so we can figure out why it's not working :)

                2. A
                  In reply toJesperA⬆:
                  Andrew Downes @Andrew_Downes
                    2020-11-27 03:04:07.839Z

                    Thanks for the reply Jesper but I'm in way over my head. Its working the way I'd like, a different layer depending on what modifier key I have pressed, and because I have 2 decks I can have the master key on one and the relevant information on the other.
                    What I was hoping to do was have 1 key with multiple functions, for example , I have 4 different I/O setups depending on what I'm doing and at the moment they use 4 keys on the deck, what I would like would be to have all the I/O settings on 1 key and, depending on the modifier, have the I/O I need change.
                    I'll do some more learning about javascript and come back to it.
                    Thanks again

                    1. R
                      In reply toJesperA⬆:
                      Robert Sörling @robertsorling
                        2020-11-29 22:44:31.502Z

                        Hi! I've tried your script and it's awesome and work great. Loving it! However, I have decks set up that are triggered by windows - for example the Playback Engine window triggers a Deck with shortcuts to different H/W BUffer Sizes. With your script running in the background it overrides the Playback Engine Deck. Is there a way to temporarly paus the "modifiers script" in these kind of situations?

                        Thanks!

                        1. Hey M. Glad to hear it's working.

                          Sure there is. Just do something like this:

                          sf.engine.runInBackground(function () {
                              while (true) {
                                  // This checks for a user cancellation.
                                  sf.engine.checkForCancellation();
                          
                                  // This will break the loop if finder is not in focus 
                                  if (sf.ui.frontmostApp.activeBundleID != app.activeBundleID)
                                      break;
                          
                                  // This will break the loop if the window with the title Playback Engine is in focus. I don't know the exact name of the window in Pro Tools.  
                                  if (app.focusedWindow.title.value == Playback Engine'')
                                      break;
                          
                                  // Checking what streamdeck to load
                                  if (event.keyboardState.asString == 'ctrl') sf.soundflow.runCommand({ commandId: ctrl })
                                  else if (event.keyboardState.asString == 'cmd') sf.soundflow.runCommand({ commandId: cmd })
                                  else sf.soundflow.runCommand({ commandId: normal })
                          
                                  // This will do so we check every 100 ms if there's been a "change"
                                  sf.wait({
                                      executionMode: 'Background',
                                      intervalMs: 100,
                                      onError: 'Continue'
                                  });
                              }
                          });
                          

                          And you might then wanna add a window trigger for the script when the Playback Engine window is closed...

                        2. R
                          In reply toJesperA⬆:
                          Robert Sörling @robertsorling
                            2020-12-07 22:06:24.738Z

                            Hi, it's me again. :) Thank you for your previous info!

                            I'm breaking my back trying to figure out to a similar thing, but this time breaking the loop if a certain Deck is showing. Like if I want to acces a Deck that is "outside" the modifier loop. Is this possible too? Been searching the forum like crazy but my scripting scills are still somewhat limited. :)

                            Thanks

                            1. So you mean you are showing another deck and when doing so you want to break the loop?

                              So are you manually selecting the deck to show? Or how is deck "outside" being selected?

                            2. R
                              In reply toJesperA⬆:
                              Robert Sörling @robertsorling
                                2020-12-08 20:56:09.063Z

                                I have set up a series of decks (lets call them "Mod Decks") running in ProTools, dynamicly changing depending on what modifier key/keys are pressed. Within in one of the decks (the "normal" one, showing when no modifier keys are pressed) I need one stream deck button (lets call it X Button ) that triggers a deck (lets call it "Outside Deck") that is not part of the mod decks. When pressing X Button I want break the Mod Decks - loop, and show the Outside Deck. As for now when pressing the X Button the Outside Deck just briefly flashes as the Mod Decks script overrides it. Hope it makes sense. ;) Thanks!

                                1. Okay great. Should not be too hard, but one more thing then, when do you want the script to start up again and the mod decks to continue? Is that when you change PT window or closing the "outside" deck on a button?

                                  One initial way you already can do this is by having that outside deck as a folder on the main deck. I believe SF even recalls if you've had the folder open as the last thing, when changing between decks.
                                  But you would have to rebuild that specific deck in a folder on your Main Mod Deck.

                                2. R
                                  In reply toJesperA⬆:
                                  Robert Sörling @robertsorling
                                    2020-12-08 22:56:32.712Z

                                    Dude, what an Occam's razor -moment. Of course, using a folder is the solution! Thank you Jesper, I really should have figured that out myself. :P

                                    1. Haha. Well, that's what the forum is for. Glad I could help!

                                    2. M
                                      In reply toJesperA⬆:
                                      Martin Pavey @Martin_Pavey
                                        2021-01-16 16:14:29.289Z

                                        Wow! This is potentially great!
                                        I was just starting to make a surface with SF to get more keys, but this will give me layers of keys switching on my Streamdecks instead. ;-)
                                        As you say Jesper, it would be great to have this as a built in feature in SF please.
                                        Is there any penalty for running these constantly looping scripts in the background?

                                        1. Hey Martin.

                                          I don't think there's much "penalty" for doing this. I've at least run it stabile without experiencing any issue.

                                          The more people need a feature the faster it comes, so good you've chimed in here with the potential that this brings.

                                          I'll update the thread / create a new, when there might be a new and smarter way to do these things.

                                        2. M
                                          In reply toJesperA⬆:
                                          Martin Pavey @Martin_Pavey
                                            2021-01-16 20:18:01.086Z

                                            Excellent! Thanks Jasper.
                                            4 layers of 32 keys on a Streamdeck and I may never want for extra shortcut keys again.
                                            I say that now of course ;-)

                                            1. A
                                              In reply toJesperA⬆:
                                              Andrew Downes @Andrew_Downes
                                                2021-02-28 23:05:51.437Z

                                                Hi Jesper
                                                I have finally managed to figure out how to get the Icons to change depending on the modifier keys and are now wondering if I am limited to just one set of modifier keys per app (Pro Tools) or whether I can have dfferent sets depending on which deck I have open within that app?
                                                Here's an example,
                                                I have a plugins deck folder and inside that are options for choosing and/or bypassing plugins etc. There are other folders within that main deck that are there for other plugin related functions. Once that main deck is open could you apply the modifier keys to work within that deck alone, and I mean the icon change part as I have a set of keys to get me to the folder I want already working.

                                                I have seen how to break the loop when there are active windows open on the screen but wonder if you could achieve the same thing with a specific deck?

                                                I hope this makes sense
                                                Thank you

                                                1. Hey Andrew.

                                                  So little unsure if I understand you scenario 100%.. Maybe if you shared your code with some explanations of what you want.
                                                  But here goes some help from what I'm reading out of your text.


                                                  I could imagine a couple of different ways to do this (from what I understand you want to do). I'm sure one will be better then the others but would probably be a bit trial and error.

                                                  One way that this could be done, as I read you text is to stop the script when you select a specific deck. We have something that's called globalState which we could use for this as it's variables that is "saved" in to SF and therefor works across scripts.
                                                  Now in our run script before running it, we will add a bit of code. (iv'e just taken some un altered code from above for the run script). And we will also add a line for the script to run if the globalState changes.

                                                  // we start by making sure our globalState is false. Let's call the 
                                                  globalState.stopProToolsScript = false
                                                  
                                                  sf.engine.runInBackground(function () {
                                                      while (true) {
                                                          // This checks for a user cancellation.
                                                          sf.engine.checkForCancellation();
                                                  
                                                          // This will break the loop if finder is not in focus 
                                                          if (sf.ui.frontmostApp.activeBundleID != app.activeBundleID)
                                                              break;
                                                  
                                                          // This will break the loop if globalState.stopProToolsScript is set to "true" 
                                                          if (globalState.stopProToolsScript )
                                                              break;
                                                  
                                                          // This will break the loop if the window with the title Playback Engine is in focus. I don't know the exact name of the window in Pro Tools.  
                                                          if (app.focusedWindow.title.value == Playback Engine'')
                                                              break;
                                                  
                                                          // Checking what streamdeck to load
                                                          if (event.keyboardState.asString == 'ctrl') sf.soundflow.runCommand({ commandId: ctrl })
                                                          else if (event.keyboardState.asString == 'cmd') sf.soundflow.runCommand({ commandId: cmd })
                                                          else sf.soundflow.runCommand({ commandId: normal })
                                                  
                                                          // This will do so we check every 100 ms if there's been a "change"
                                                          sf.wait({
                                                              executionMode: 'Background',
                                                              intervalMs: 100,
                                                              onError: 'Continue'
                                                          });
                                                      }
                                                  });
                                                  

                                                  Now creating a script that will select you main deck and add:
                                                  globalState.stopProToolsScript = true
                                                  to that script (you wouldn't be able to do this with a macro, but actually something I'll request as a feature) you can stop the script checking for the modifier keys. You would though have to have a way to start the script again, or just change focus off and on pro tools.

                                                  In case you want the script to keep running, but want other new decks to be shown on modifiers after selecting the "main deck" we would do it a little different, like so:

                                                  // we start by making sure our globalState is false. Let's call the 
                                                  globalState.stopProToolsScript = false
                                                  
                                                  sf.engine.runInBackground(function () {
                                                      while (true) {
                                                          // This checks for a user cancellation.
                                                          sf.engine.checkForCancellation();
                                                  
                                                          // This will break the loop if finder is not in focus 
                                                          if (sf.ui.frontmostApp.activeBundleID != app.activeBundleID)
                                                              break;
                                                  
                                                          // This will break the loop if the window with the title Playback Engine is in focus. I don't know the exact name of the window in Pro Tools.  
                                                          if (app.focusedWindow.title.value == Playback Engine'')
                                                              break;
                                                  
                                                          // Checking what streamdeck to load
                                                          // if globalState.stopProToolsScript is set to "true" it will show some different ones 
                                                          if (globalState.stopProToolsScript ){
                                                              // Add some other streamdecks here with your modifiers. 
                                                              // In order to return to the "previous/first" layer you'll have to set the globalState to false again
                                                          }
                                                          else {
                                                              if (event.keyboardState.asString == 'ctrl') sf.soundflow.runCommand({ commandId: ctrl })
                                                              else if (event.keyboardState.asString == 'cmd') sf.soundflow.runCommand({ commandId: cmd })
                                                              else sf.soundflow.runCommand({ commandId: normal })
                                                          }
                                                          // This will do so we check every 100 ms if there's been a "change"
                                                          sf.wait({
                                                              executionMode: 'Background',
                                                              intervalMs: 100,
                                                              onError: 'Continue'
                                                          });
                                                      }
                                                  });
                                                  

                                                  Let me know if this helps you going forward.

                                                  1. AAndrew Downes @Andrew_Downes
                                                      2021-03-02 22:27:25.582Z

                                                      Hi Jesper

                                                      I have included a version of the code so I can try and make sense to you as to what I'm trying to achieve.

                                                      var app = sf.ui.proTools
                                                      
                                                      var normal = 'user:cka3b7jfs0000np1013vgrrtd:cka3bloei0003np10cy76b7qv'
                                                      var shift = 'user:cka3b7jfs0000np1013vgrrtd:cka3b82rd0001np10d0w335u3'
                                                      var ctrl = 'user:cka3b7jfs0000np1013vgrrtd:cka3e7i550000qs10n50b2wk6'
                                                      
                                                      // If finder is unhiden start loop
                                                      sf.engine.runInBackground(function () {
                                                          while (true) {
                                                              // This checks for a user cancellation.
                                                              sf.engine.checkForCancellation();
                                                      
                                                              // This will break the loop if finder is not in focus 
                                                              if (sf.ui.frontmostApp.activeBundleID != app.activeBundleID)
                                                                  break;
                                                      
                                                      if (event.keyboardState.asString == 'shift') sf.soundflow.runCommand({ commandId: shift })
                                                      else if (event.keyboardState.asString == 'ctrl') sf.soundflow.runCommand({ commandId: ctrl })
                                                      else sf.soundflow.runCommand({ commandId: normal })
                                                      
                                                      
                                                      // This will do so we check every 100 ms if there's been a "change"
                                                              sf.wait({
                                                                  executionMode: 'Background',
                                                                  intervalMs: 100,
                                                                  onError: 'Continue'
                                                              });
                                                          }
                                                      });
                                                      
                                                      

                                                      The normal line is the normal top layer of my stream deck that has all the shortcuts I use regularly.
                                                      Shift = the plugins deck and ctrl = the sends deck.

                                                      Now within the plugins page I am running decks that access ways to remove plugins from their slot, a way to get to slots 6-10 and some Audio Suite shortcuts.
                                                      All of these decks along with 3 other decks that have particular plugins assigned to them, I have mapped to modifier keys for instant access.
                                                      So for example, When I push the “Plugins” button on my stream deck I get the top deck, but if I push “control-Plugins” I am taken to the EQ plugins or “alt-Plugins” I get to the compressor deck…you get the idea.
                                                      While this works great I have to remember which modifier key does what.

                                                      What I am trying to have happen is this

                                                      As I stated before, the “normal” stream deck is set to my regularly used Pro Tools shortcuts, the shift key is the plugins. What I would like to have happen is that when the shift key is pushed and the top plugins deck opens, the modifier keys somehow resets and the Plugins key becomes “normal” and the modifier keys that only effect the plugins pages will show on the stream deck depending on the modifier key that is pushed.

                                                      To make it even simpler, I'd like the modifier keys to call different decks and depending on what deck showing, show me those icons.

                                                      Thank you.

                                                      Main Plugins Page

                                                      Remove Inserts

                                                      Specific EQ